using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;

namespace Weever.Json.Binding
{
    class XmlJsonDictionaryWriter: XmlDictionaryWriter
    {
        private WriteState _state;
        private StreamWriter _innerWriter;

        private static readonly Regex EscapeRegex =
            new Regex(@"(?<toEscape>[^\x20-\x21\x23-\x5B\x5D-\u10FFFF])", RegexOptions.Compiled);

        private XmlWriterSettings _settings;

        private const string JsonNull = "null";
        private const string JsonTrue = "true";
        private const string JsonFalse = "false";
        
        private Stack<JsonNodeType> _stack = new Stack<JsonNodeType>(10);
        
        public XmlJsonDictionaryWriter(Stream stream)
        {
            _stack.Push(JsonNodeType.Start);
            _settings = new XmlWriterSettings();
            _innerWriter = new StreamWriter(stream, _settings.Encoding);
        }
        
        
        private void WriteStartObject()
        {
            if (_stack.TopIs(JsonNodeType.Value, JsonNodeType.EndArray, JsonNodeType.EndObject))
                _innerWriter.Write(",");
            
            _stack.Push(JsonNodeType.StartObject);
            _innerWriter.Write("{");
        }
        
        private void WriteEndObject()
        {
            _stack.PopUntil(JsonNodeType.StartObject);
            _innerWriter.Write("}");
        }

        public override void WriteStartDocument()
        {
            WriteStartObject();
            if (!_settings.OmitXmlDeclaration)
            {
                WriteXmlAttribute("version", "1.0");
                WriteXmlAttribute("encoding", _settings.Encoding.BodyName);
            }
            _state = WriteState.Prolog;
        }

        public override void WriteStartDocument(bool standalone)
        {
            WriteStartDocument();
        }

        public override void WriteEndDocument()
        {
            _state = WriteState.Start;
            WriteEndObject();
        }

        public override void WriteDocType(string name, string pubid, string sysid, string subset)
        {
            throw new NotSupportedException();
        }

        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            if (_stack.TopIs(JsonNodeType.EndObject, JsonNodeType.EndArray, JsonNodeType.Value))
                _innerWriter.Write(",");
            
            if (prefix != null)
                _innerWriter.Write(prefix + "$" + localName + ":{");
            else
                _innerWriter.Write(localName + ":{");

            _stack.Push(JsonNodeType.StartObject);

            if (ns != string.Empty)
            {
                _innerWriter.Write("ns:" + ns);
                _stack.Push(JsonNodeType.Value);
            }

            _state = WriteState.Element;
        }

        public override void WriteEndElement()
        {
            _innerWriter.Write("}");
            _stack.PopAfter(JsonNodeType.StartObject);
            _state = WriteState.Content;
        }

        public override void WriteFullEndElement()
        {
            _innerWriter.Write("}");
            _stack.PopUntil(JsonNodeType.StartObject);
            _state = WriteState.Content;
        }


        public override void WriteXmlAttribute(string localName, string value)
        {
            if (_stack.TopIs(JsonNodeType.Value, JsonNodeType.EndObject, JsonNodeType.EndArray))
                _innerWriter.Write(",");
            _stack.Push(JsonNodeType.Value);
            _innerWriter.Write(localName + ":" + value);
        }

        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            if (_stack.TopIs(JsonNodeType.Value, JsonNodeType.EndObject, JsonNodeType.EndArray))
                _innerWriter.Write(",");
            _stack.Push(JsonNodeType.Name);

            if (!string.IsNullOrEmpty(prefix))
                _innerWriter.Write(prefix + "$" + localName + ":");
            else
                _innerWriter.Write(localName + ":");
        }

        public override void WriteEndAttribute()
        {
            int x = 0;
        }

        public override void WriteCData(string text)
        {
            throw new NotImplementedException();
        }

        public override void WriteComment(string text)
        {
            throw new NotImplementedException();
        }

        public override void WriteProcessingInstruction(string name, string text)
        {
            throw new NotImplementedException();
        }

        public override void WriteEntityRef(string name)
        {
            throw new NotImplementedException();
        }

        public override void WriteCharEntity(char ch)
        {
            throw new NotImplementedException();
        }

        public override void WriteWhitespace(string ws)
        {
            throw new NotImplementedException();
        }

        public override void WriteString(string text)
        {
            _stack.Push(JsonNodeType.Value);
            _innerWriter.Write("\"" + text + "\"");
        }

        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            throw new NotImplementedException();
        }

        public override void WriteChars(char[] buffer, int index, int count)
        {
            throw new NotImplementedException();
        }

        public override void WriteRaw(char[] buffer, int index, int count)
        {
            throw new NotImplementedException();
        }

        public override void WriteRaw(string data)
        {
            if (_stack.TopIs(JsonNodeType.Value, JsonNodeType.EndObject, JsonNodeType.EndArray))
                _innerWriter.Write(",");
            _stack.Push(JsonNodeType.Value);
            _innerWriter.Write("$t:" + data);
        }

        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            throw new NotImplementedException();
        }

        public override void Close()
        {
            Flush();
            if (_settings.CloseOutput)
                _innerWriter.Close();
        }

        public override void Flush()
        {
            _innerWriter.Flush();
        }

        public override string LookupPrefix(string ns)
        {
            Console.WriteLine(ns);
            return string.Empty;
        }

        public override WriteState WriteState
        {
            get { return _state; }
        }


        public override void WriteName(string name)
        {
            // what should I do here?
            base.WriteName(name);
        }

        public override void WriteQualifiedName(string localName, string ns)
        {
            base.WriteQualifiedName(localName, ns);
        }

        public override void WriteValue(object value)
        {
            if (value == null)
                _innerWriter.Write(JsonNull);
            else 
                base.WriteValue(value);
        }

        public override void WriteValue(string value)
        {
            _innerWriter.Write(value);
        }

        public override void WriteValue(bool value)
        {
            if (value)
                _innerWriter.Write(JsonTrue);
            else 
                _innerWriter.Write(JsonFalse);
        }

        public override void WriteValue(DateTime value)
        {
            // override when find out what to do
            _innerWriter.Write(value);
        }

        public override void WriteValue(double value)
        {
            _innerWriter.Write(value);
        }

        public override void WriteValue(float value)
        {
            _innerWriter.Write(value);
        }

        public override void WriteValue(decimal value)
        {
            _innerWriter.Write(value);
        }

        public override void WriteValue(int value)
        {
            _innerWriter.Write(value);
        }

        public override void WriteValue(long value)
        {
            _innerWriter.Write(value);
        }

        public override XmlWriterSettings Settings
        {
            get { return _settings; }
        }

        public override XmlSpace XmlSpace
        {
            get { return base.XmlSpace; }
        }

        public override string XmlLang
        {
            get { return base.XmlLang; }
        }
    }
}
